package controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.fasterxml.jackson.databind.ObjectMapper;

@Controller
public class RuleController {

    private final AtomicLong ruleIdGenerator = new AtomicLong();

    private final List<Map<String, Object>> currentRules = Collections.synchronizedList(new ArrayList<Map<String, Object>>());

    @Resource(name = "outboundRules")
    private BlockingQueue<Map<String, Object>> outboundRules;

    @Resource(name = "objectMapper")
    private ObjectMapper jsonParser;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        model.addAttribute("activeRules", getRulesByState("active"));
        model.addAttribute("inactiveRules", getRulesByState("inactive"));
        return "index";
    }

    @RequestMapping(value = "/rule", method = RequestMethod.POST, params = { "action=create", "type=DiscountForManyCallsRule" })
    public String createNewDiscountForManyCallsRule(@RequestParam int numberOfCalls, @RequestParam String period, @RequestParam String type) {
        final Map<String, Object> newRule = new HashMap<String, Object>();
        newRule.put("id", ruleIdGenerator.incrementAndGet());
        newRule.put("type", type);
        newRule.put("state", "active");
        newRule.put("numberOfCalls", numberOfCalls);
        newRule.put("period", period);

        currentRules.add(newRule);

        notifyRuleChange(newRule, "activate");

        return "redirect:/";
    }

    @RequestMapping(value = "/rule", method = RequestMethod.POST, params = { "action=create", "type=DiscountForFailureRule" })
    public String createNewDiscountForFailureRule(@RequestParam int numberOfCalls, @RequestParam String type) {
        final Map<String, Object> newRule = new HashMap<String, Object>();
        newRule.put("id", ruleIdGenerator.incrementAndGet());
        newRule.put("type", type);
        newRule.put("state", "active");
        newRule.put("numberOfCalls", numberOfCalls);

        currentRules.add(newRule);

        notifyRuleChange(newRule, "activate");

        return "redirect:/";
    }

    @RequestMapping(value = "/rule", method = RequestMethod.POST, params = { "action=create", "type=DiscountForPopularUsersRule" })
    public String createNewDiscountForPopularUsersRule(@RequestParam int timeDuration, @RequestParam String hoursMinutes, @RequestParam String type) {
        final Map<String, Object> newRule = new HashMap<String, Object>();
        newRule.put("id", ruleIdGenerator.incrementAndGet());
        newRule.put("type", type);
        newRule.put("state", "active");
        newRule.put("timeAmount", timeDuration);
        newRule.put("timeUnit", hoursMinutes);

        currentRules.add(newRule);

        notifyRuleChange(newRule, "activate");

        return "redirect:/";
    }

    @RequestMapping(value = "/rule", method = RequestMethod.POST, params = { "action=activate" })
    public String activateRule(long ruleId) {
        final Map<String, Object> updatedRule = changeRuleState(ruleId, "active");
        notifyRuleChange(updatedRule, "activate");
        return "redirect:/";
    }

    @RequestMapping(value = "/rule", method = RequestMethod.POST, params = { "action=deactivate" })
    public String deactivateRule(long ruleId) {
        final Map<String, Object> updatedRule = changeRuleState(ruleId, "inactive");
        notifyRuleChange(updatedRule, "deactivate");
        return "redirect:/";
    }

    private List<Map<String, Object>> getRulesByState(String state) {
        final List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        for (final Map<String, Object> rule : currentRules) {
            if (rule.get("state").equals(state)) {
                result.add(rule);
            }
        }
        return result;
    }

    private void notifyRuleChange(Map<String, Object> rule, String action) {
        final Map<String, Object> data = new HashMap<String, Object>(rule);
        data.put("action", action);

        try {
            outboundRules.put(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, Object> changeRuleState(long ruleId, String newState) {
        Map<String, Object> rule = null;
        for (final Map<String, Object> aRule : currentRules) {
            if ((Long) aRule.get("id") == ruleId) {
                rule = aRule;
                break;
            }
        }
        rule.put("state", newState);
        return rule;
    }

}
